package games.eightPuzzle;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;

import commonDataStructures.BinaryHeap;

import games.eightPuzzle.states.SlidingPuzzleState;

/**
 * @author Avishai
 * this class represents a solutions bank who store the found
 * solutions and get print the best solution at the bank
 */
/**
 * @author Avishai
 *
 */
public class SolutionBank {

	private static final String NO_SOLUTION = "-1";
	private static String outputPath = "";
	private static boolean hasSolution=false;
	private static BinaryHeap<SolutionInfo> solutions = new BinaryHeap<SolutionInfo>();
	private static ArrayList<SolutionInfo> tmpSolutions = new ArrayList<SolutionInfo>();
	public static long ctr = 0;
	public static void init(){
		outputPath="";
		hasSolution=false;
		solutions.init();
		tmpSolutions.clear();
	}
	/**
	 * adds a solution to the bank
	 * @param sol
	 */
	public static synchronized void addSolution(SolutionInfo sol){
		if (sol.hasSolution()){
			hasSolution=true;
		}
		tmpSolutions.add(sol);
	}

	/**
	 * set the path to print the solution to
	 * @param path
	 */
	public static void setOutputPath(String path){
		outputPath = path;
	}
	
	/**
	 * gets the best solution from the bank
	 * @return
	 */
	private static SolutionInfo getTmpSolution(){
		if (tmpSolutions.size()==0){
			return null;
		}

		SolutionInfo bestSol = tmpSolutions.get(0);
		//get the best solution out of all the solutions
		//the best solution is the one with the shortest
		//length of solution and the smallest node expantions
		for(int i=1;i<tmpSolutions.size();i++){
			SolutionInfo sol = tmpSolutions.get(i);
			
			if (!sol.hasSolution()) continue;
			
			if (bestSol.getSolutionLength()==sol.getSolutionLength()){
				if (sol.getNodesExpantionNumber()<bestSol.getNodesExpantionNumber()){
					bestSol = sol;
				}
			}else if(bestSol.getSolutionLength()>sol.getSolutionLength()){
				bestSol = sol;
			}
		}
		return bestSol;
	}

	public static void loadFinalSolution(){
		SolutionInfo sol = getTmpSolution();
		if (sol!=null){
			solutions.insert(sol);
			//solutions.add(sol);
		}
		tmpSolutions.clear();
	}
	
	public static void loadAllSolutions(){
		for(SolutionInfo sol:tmpSolutions){
			if (sol!=null){
				solutions.insert(sol);
			}
		}
	}
	/**
	 * check if a solution has been found
	 * @return
	 */
	public static boolean hasSolution(){
		return hasSolution;
	}
	
	/**
	 * prints the solution to a file 
	 */
	public static void printSolution(){
		BufferedWriter bw = null;
		try{
			bw = new BufferedWriter(new FileWriter(outputPath));
			SolutionInfo bestSol = (solutions.isEmpty()?null:solutions.remove());
			if (bestSol==null){
				bw.write("0");
				bw.write(NO_SOLUTION);
			}else{
				bw.write(String.valueOf(bestSol.getNodesExpantionNumber()));
				bw.newLine();
				bw.write("Time in ms: "+String.valueOf(bestSol.getTime()));
				bw.newLine();
				bw.write("Mem usage: " + String.valueOf(ctr));
				bw.newLine();
				//some pricess found a solution
				if (bestSol.hasSolution()){
					bw.write(String.valueOf(bestSol.getSolutionLength()));
					bw.newLine();
					ArrayList<SlidingPuzzleState> solution = bestSol.getSolutionTrace();
					if (bestSol.isReversed()){
						for(int i=solution.size()-1;i>=0;i--){
							solution.get(i).print(bw);
						}
					}else{
						for(SlidingPuzzleState state:solution){
							state.print(bw);
						}
					}
				}else{
					bw.write(NO_SOLUTION);
				}
			}
		}catch(Exception e){}
		finally{
			try{
				bw.close();
			}catch (Exception e){}
		}
		System.exit(0);
	}
	
	public static List<SolutionInfo> getSolutionsList(){
		ArrayList<SolutionInfo> res = new ArrayList<SolutionInfo>();
		while (!solutions.isEmpty()){
			res.add(solutions.remove());
		}
		
		return res;
	}

}
